<?php
/**
 * Copyright (c) [2019] [吴跃忠]
 * [selibra] is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 * http://license.coscl.org.cn/MulanPSL
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
 * PURPOSE.
 * See the Mulan PSL v1 for more details.
 */

namespace Selibra\Http\Server;


use Selibra\Di\Annotations\Autowired;
use Selibra\Config\Config;
use Selibra\Di\Container\ContainerCollector;
use Selibra\Di\Container\Exception\MetadataNotFoundException;
use Selibra\Di\DI;
use Selibra\Event\EventTrigger;
use Selibra\Http\Annotations\HttpServerOnStart;
use Selibra\Http\Config\HttpServerBootstrap;
use Selibra\Http\Events\HttpOnManagerStartEvent;
use Selibra\Http\Events\HttpOnRequestEndEvent;
use Selibra\Http\Events\HttpOnRequestEvent;
use Selibra\Http\Events\HttpOnRequestExceptionEvent;
use Selibra\Http\Events\HttpOnStartEvent;
use Selibra\Http\Events\HttpOnWorkerStartEvent;
use Selibra\Http\Events\HttpStartBeforeEvent;
use Selibra\Http\Protocol\HttpServerInterface;
use Selibra\Http\Protocol\HttpServerOnStartInterface;
use Swoole\Coroutine;
use Swoole\Http\Request as SwooleHttpRequest;
use Swoole\Http\Response as SwooleHttpResponse;
use Swoole\Http\Server as SwooleHttpServer;
use Selibra\Http\Exception\PageNotFoundException;
use Selibra\Http\Lib\Actuator;
use Selibra\Http\Lib\Request;
use Selibra\Http\Lib\Response;
use Selibra\Task\Consumer;
use Selibra\Tools\Console;
use Swoole\WebSocket\Server;

class HttpServer implements HttpServerInterface
{


    /**
     * @var HttpServerBootstrap
     */
    #[Autowired]
    public HttpServerBootstrap $httpServerBootstrap;


    /**
     * @var SwooleHttpServer
     */
    protected static SwooleHttpServer $httpServer;


    /**
     * @var SwooleHttpServer[]
     */
    protected static array $httpServerCoroutine = [];


    /**
     * 启动HttpServer服务
     * @return void
     */
    public function start()
    {
        try {
            $this->go();
        } catch (\Throwable $throwable) {
            Console::log($throwable);
        }
    }

    /**
     * 执行一个HttpServer服务
     * @return void
     */
    private function go()
    {

        $httpServer = new SwooleHttpServer($this->httpServerBootstrap->getIp(), $this->httpServerBootstrap->getPort());
        $httpServer->set(Config::get('application.httpserver'));

        $httpServer->on('start', function ($httpServer) {
            EventTrigger::listener((new HttpOnStartEvent($httpServer)));
            Coroutine::create(function () {
                /** @var HttpServerOnStartInterface[] $httpServerOnStarts */
                $httpServerOnStartsArray = ContainerCollector::getContainer()->collector()
                    ->selectObjectByContainerGroup(HttpServerOnStart::class);
                foreach ($httpServerOnStartsArray as $httpServerOnStart) {
                    // 协程执行
                    go(function () use ($httpServerOnStart) {
                        Console::log("初始进程-CID:", Coroutine::getCid(), $httpServerOnStart);
                        $httpServerOnStart = DI::getObjectContext($httpServerOnStart);
                        $httpServerOnStart->start();
                    });
                }
            });
        });

        $httpServer->on('request', function (SwooleHttpRequest $request, SwooleHttpResponse $response) use ($httpServer) {
            try {
                $this->onRequest($request, $response, $httpServer);
            } catch (\Error $error) {
                Console::log($error);
            }
        });

        $httpServer->on('managerStart', function ($server) {
            EventTrigger::listener((new HttpOnManagerStartEvent($server)));
        });

        $httpServer->on('workerStart', function ($server, $worker_id) {
            EventTrigger::listener((new HttpOnWorkerStartEvent($server, $worker_id)));
        });

        // 如果开启了Task，使用Task
        if (!empty(Config::get('application.httpserver.task_worker_num'))) {
            $httpServer->on('task', function ($server, $task) {
                $taskConsumer = new Consumer();
                $taskConsumer->exec($server, $task);
                $task->finish("OK");
            });
            $httpServer->on('finish', function ($serv, $task_id, $data) {
                echo "AsyncTask[$task_id] Finish: $data" . PHP_EOL;
            });
        }
        self::$httpServer = $httpServer;
        // 服务启动之前的事件
        EventTrigger::listener((new HttpStartBeforeEvent($httpServer)));
        $httpServer->start();
    }


    /**
     * @param SwooleHttpRequest $request
     * @param SwooleHttpResponse $response
     * @param SwooleHttpServer $httpServer
     * @return void|null
     */
    private function onRequest(SwooleHttpRequest $request, SwooleHttpResponse $response, SwooleHttpServer $httpServer)
    {
        // 注意！$response 不能通过引用的方式传递，将$response赋值到Response对象中再进行操作
        $requestLib = new Request($request, self::$httpServer);
        $responseLib = new Response($response);
        // 启动ob缓存处理器，将输出内容进行统一管理
        try {
            // 请求开始
            EventTrigger::listener((new HttpOnRequestEvent($requestLib, $responseLib)));
            $actuator = new Actuator($requestLib, $responseLib);
            $responseLib = $actuator->exec();
        } catch (PageNotFoundException $pageNotFoundException) {
            $response->header('content-type', 'text/html; charset=utf-8');
            $response->status(404);
            $response->end('您的访问的页面丢失了，请返回上一层看看吧。');
            return null;
        } catch (\Throwable $exception) {
            EventTrigger::listener((new HttpOnRequestExceptionEvent($exception, $requestLib, $responseLib)));
            $response->header('content-type', 'text/html; charset=utf-8');
            
            $responseLib->setCode(500)->setStatus(500);
            // Debug判定，如果开启Debug，将直接输出$exception
            $debugEnable = Config::get('application.http.debug');
            if ($debugEnable) {
                $responseLib->setMessage($exception->getMessage());
                $responseLib->setContent([
                    'trace' => $exception->getTraceAsString()
                ]);
            } else {
                $responseLib->setMessage('error');
            }
        }
        // 返回
        $this->end($requestLib, $response, $responseLib);
        unset($response);
        unset($request);
    }

    /**
     * @param $response
     * @param $responseLib
     * @return void
     */
    public function end(Request $requestLib, SwooleHttpResponse $response, ?Response $responseLib = null)
    {
        // 头部信息设置
        if (!empty($responseLib)) {
            foreach ($responseLib->selectHeaders() as $key => $value) {
                $response->header($key, $value);
            }
        }

        $response->status($responseLib->getStatus());
        $response->end($responseLib->getResponseData());
        EventTrigger::listener((new HttpOnRequestEndEvent($requestLib, $responseLib)));
    }


    public function stop()
    {
        // TODO: Implement stop() method.
    }

    public function restart()
    {
        // TODO: Implement restart() method.
    }

    /**
     * @return SwooleHttpServer
     */
    public static function getHttpServer(): SwooleHttpServer
    {
        return self::$httpServer;
    }
}
